package com.wva.common.exception;

import com.wva.common.status.Bind;
import com.wva.common.status.StatusEnum;
import com.wva.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import java.util.Set;

/**
 * 异常处理器，用于封装错误为接口信息并响应
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 系统异常、业务异常
     */
    @ExceptionHandler(BaseException.class)
    public R<Object> base(BaseException e) {
        //系统级异常，严重异常
        if (e instanceof SystemException) log.error("SystemException: ", e);
        //业务异常，一般异常
        if (e instanceof BusinessException) log.warn("BusinessException: ", e);
        return new R<>(e.getStatus());
    }

    /**
     * validated注解校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Object> validate(MethodArgumentNotValidException e) {
        log.warn("validated注解校验异常", e);

        //如果含有payload
        ObjectError objectError = e.getAllErrors().get(0);
        //TODO
        ConstraintViolation constraintViolation = objectError.unwrap(ConstraintViolation.class);
        Set<Class<? extends Payload>> payloads = constraintViolation.getConstraintDescriptor().getPayload();
        for (Class<? extends Payload> payload : payloads) {
            Bind bind = payload.getAnnotation(Bind.class);
            if (bind != null) {
                return new R<>(bind.status());
            }
        }

        //读取具体校验错误（只返回第一条错误信息）
        R<Object> result = new R<>(StatusEnum.PARAMS_CHECK_FAIL);
        result.message(e.getAllErrors().get(0).getDefaultMessage());

        return result;
    }

    /**
     * 参数校验异常
     * @param e
     */
    @ExceptionHandler(BindException.class)
    R<Object> bindException(BindException e) {
        return R.err(e.getAllErrors().get(0).getDefaultMessage());
    }


    /**
     * springMVC接口参数检验异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R<Object> springMVC(Exception e) {
        log.warn("spring检验参数时发生异常", e);
        return new R<>(StatusEnum.PARAMS_MISSING);
    }

    /**
     * 捕获其它未知异常
     */
    @ExceptionHandler(Exception.class)
    public R<Object> other(Exception e) {
        log.error("未知异常", e);
        return new R<>(StatusEnum.UNKNOWN_ERROR);
    }
}
